package com.atguigu.yygh.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.common.config.MqConst;
import com.atguigu.yygh.common.config.RabbitService;
import com.atguigu.yygh.common.handler.YyghException;
import com.atguigu.yygh.enums.OrderStatusEnum;
import com.atguigu.yygh.hosp.client.HospitalFeignClient;
import com.atguigu.yygh.model.order.OrderInfo;
import com.atguigu.yygh.model.user.Patient;
import com.atguigu.yygh.order.mapper.OrderInfoMapper;
import com.atguigu.yygh.order.service.OrderService;
import com.atguigu.yygh.order.service.WeixinService;
import com.atguigu.yygh.order.utils.HttpRequestHelper;
import com.atguigu.yygh.user.client.PatientFeignClient;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.msm.MsmVo;
import com.atguigu.yygh.vo.order.OrderCountQueryVo;
import com.atguigu.yygh.vo.order.OrderCountVo;
import com.atguigu.yygh.vo.order.OrderMqVo;
import com.atguigu.yygh.vo.order.OrderQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author 杨林
 * @create 2022-11-17 15:02 星期四
 * description:
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private PatientFeignClient patientFeignClient;

    @Autowired
    private HospitalFeignClient hospitalFeignClient;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    RabbitService rabbitService;

    @Autowired
    private WeixinService weixinService;

    //@Autowired
    //private RabbitTemplate rabbitTemplate;

    @Override
    public long saveOrder(String scheduleId, Long patientId) {

        //根据scheduleId查询schedule  根据patientId 查询patientId
        Patient patient = patientFeignClient.getPatient(patientId);
        ScheduleOrderVo scheduleOrderVo = hospitalFeignClient.getScheduleOrderVo(scheduleId);

        //0.查询数据  为调用医院端接口做准备
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("hoscode", scheduleOrderVo.getHoscode());
        paramMap.put("depcode", scheduleOrderVo.getDepcode());
        paramMap.put("hosScheduleId", scheduleOrderVo.getHosScheduleId());
        paramMap.put("reserveDate", scheduleOrderVo.getReserveDate());
        paramMap.put("reserveTime", scheduleOrderVo.getReserveTime());
        paramMap.put("amount", scheduleOrderVo.getAmount());
        paramMap.put("name", patient.getName());
        paramMap.put("sex", patient.getSex());
        paramMap.put("birthdate", patient.getBirthdate());
        paramMap.put("phone", patient.getPhone());
        paramMap.put("isMarry",patient.getIsMarry() );
        paramMap.put("provinceCode", patient.getProvinceCode());
        paramMap.put("cityCode", patient.getCityCode());
        paramMap.put("districtCode", patient.getDistrictCode());
        paramMap.put("address", patient.getAddress());
        paramMap.put("contactsName", patient.getContactsName());
        paramMap.put("contactsCertificatesType", patient.getContactsCertificatesType());
        paramMap.put("contactsCertificatesNo", patient.getContactsCertificatesNo());
        paramMap.put("contactsPhone", patient.getContactsPhone());
        paramMap.put("isInsure", patient.getIsInsure());
        paramMap.put("timestamp", HttpRequestHelper.getTimestamp());
        paramMap.put("sign", "");

        //1.调用医院接口  创建订单 返回数据
        //根据医院编号  查询医院设置 取出url（远程服务调用）
        String hoscode = scheduleOrderVo.getHoscode();
        String api_Url = hospitalFeignClient.getApiUrlByHoscode(hoscode);
        JSONObject result = HttpRequestHelper.sendRequest(paramMap, "http://" + api_Url + "/order/submitOrder");

        //1.1判断医院端返回的结果
        if (result.getInteger("code") == 200){  //说明医院端订单创建成功

            //获取医院端返回的业务数据
            JSONObject data = result.getJSONObject("data");
            String recordId = data.getString("hosRecordId"); //预约记录唯一标识
            Integer number = data.getInteger("number");  //预约序号
            Integer reservedNumber = data.getInteger("reservedNumber");  //排班可预约数
            Integer availableNumber = data.getInteger("availableNumber"); //排班剩余预约数
            String fetchTime = data.getString("fetchTime"); //取号时间
            String fetchAddress = data.getString("fetchAddress");  //取号地址

            //创建平台端自己的订单
            OrderInfo orderInfo = new OrderInfo();
            //生成一个订单号
            String outTradeNo = System.currentTimeMillis() + "" + new Random().nextInt(100);
            orderInfo.setOutTradeNo(outTradeNo);

            BeanUtils.copyProperties(scheduleOrderVo, orderInfo);

            orderInfo.setScheduleId(scheduleId);
            orderInfo.setUserId(patient.getUserId());
            orderInfo.setPatientId(patientId);
            orderInfo.setPatientName(patient.getName());
            orderInfo.setPatientPhone(patient.getPhone());
            //订单状态
            orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());
            //平台端存储医院端订单编号的字段
            orderInfo.setHosRecordId(recordId);
            orderInfo.setNumber(number);
            orderInfo.setFetchTime(fetchTime);
            orderInfo.setFetchAddress(fetchAddress);

            orderInfo.setCreateTime(new Date());
            orderInfo.setUpdateTime(new Date());
            //保存如数据库
            baseMapper.insert(orderInfo);

            //3.挂号成功后  跟新mg中号源的数量  + 给用户短信通知 -----> 通过MQ异步处理 实现解耦和流量削峰
            this.afterSaveOrder(reservedNumber,availableNumber,scheduleId,orderInfo);

            return orderInfo.getId();

        }else{  //挂号失败
            throw new YyghException(20001,"预约挂号失败");
        }

    }



    private void afterSaveOrder(Integer reservedNumber, Integer availableNumber, String scheduleId, OrderInfo orderInfo) {
        //发送mq信息 更新号源和发送短信
        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setAvailableNumber(availableNumber);
        orderMqVo.setReservedNumber(reservedNumber);
        orderMqVo.setScheduleId(scheduleId);

        //短信提示
        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(orderInfo.getPatientPhone());  //就诊人电话
//        msmVo.setTemplateCode();  //发送短信的类型
        msmVo.getParam().put("message","挂号成功");
        orderMqVo.setMsmVo(msmVo);

        //向rabbitmq发送消息
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
        //rabbitTemplate.convertAndSend(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);
    }

    //获取订单详情
    @Override
    public OrderInfo getOrderInfo(Long id) {

        if (StringUtils.isEmpty(id)){
            throw new YyghException(20001, "订单id不能为空");
        }
        OrderInfo orderInfo = orderInfoMapper.selectById(id);
        this.packOrderInfo(orderInfo);
        return orderInfo;
    }

    //实现分页列表
    //条件查询带分页
    @Override
    public Page<OrderInfo> selectPage(Page<OrderInfo> pageParam, OrderQueryVo orderQueryVo) {

        String keyword = orderQueryVo.getKeyword(); //医院名称
        String orderStatus = orderQueryVo.getOrderStatus(); //订单状态
        Long patientId = orderQueryVo.getPatientId();  //就诊人id
        Long userId = orderQueryVo.getUserId();  //用户id
        String createTimeBegin = orderQueryVo.getCreateTimeBegin();
        String createTimeEnd = orderQueryVo.getCreateTimeEnd();
        //String outTradeNo = orderQueryVo.getOutTradeNo();  //订单号
        //String patientName = orderQueryVo.getPatientName();
        String reserveDate = orderQueryVo.getReserveDate();  //安排时间

        //非空校验
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(keyword)){
            queryWrapper.like("hosname", keyword);
        }

        if (!StringUtils.isEmpty(orderStatus)){
            queryWrapper.eq("order_status", orderStatus);
        }

        if(!StringUtils.isEmpty(userId)) {
            queryWrapper.eq("user_id",userId);
        }

        if(!StringUtils.isEmpty(patientId)) {
            queryWrapper.eq("patient_id",patientId);
        }

        if(!StringUtils.isEmpty(reserveDate)) {
            queryWrapper.ge("reserve_date",reserveDate);
        }

        if(!StringUtils.isEmpty(createTimeBegin)) {
            queryWrapper.ge("create_time",createTimeBegin);
        }
        if(!StringUtils.isEmpty(createTimeEnd)) {
            queryWrapper.le("create_time",createTimeEnd);
        }

        Page<OrderInfo> pages = baseMapper.selectPage(pageParam, queryWrapper);
        List<OrderInfo> records = pages.getRecords();
        for (OrderInfo record : records) {
            packOrderInfo(record);
        }
        return pages;
    }


    /**
     * 取消订单的方法  （根据id）
     * @param orderId
     * @return
     */
    @Override
    public Boolean cancelOrder(Long orderId) {
        //查询订单
        OrderInfo orderInfo = this.getById(orderId);

        //判断是否超过退号时间  （超过退号时间不能取消）
        //获取该订单的退号时间
        DateTime quitTime = new DateTime(orderInfo.getQuitTime());
        if (quitTime.isBeforeNow()){  //表明退号时间在当前时间之前  （即退号时间已过）
            throw new YyghException(20001, "退号时间已过，无法取消订单");
        }

        //调用医院端接口 将医院端的订单状态改成 -1（已取消）  修改医院端的号源数量  +1
        Map<String,Object> map = new HashMap<>();
        map.put("hoscode", orderInfo.getHoscode());
        map.put("hosRecordId",orderInfo.getHosRecordId());
        String api_Url = hospitalFeignClient.getApiUrlByHoscode(orderInfo.getHoscode());
        JSONObject result = HttpRequestHelper.sendRequest(map, "http://" + api_Url + "/order/updateCancelStatus");

        //根据医院端返回的参数判断操作是否成功
        if (result.getInteger("code") != 200){  //调用医院端接口失败
            throw new YyghException(20001, "退款失败");
        }else{  //退款成功的情况
            //如果订单已经支付  则退款
            if (orderInfo.getOrderStatus().intValue() == OrderStatusEnum.PAID.getStatus()){  //表明订单已经支付
                //退款
                Boolean refund = weixinService.refund(orderId);
                if (!refund){  //表明退款失败
                    throw new YyghException(20001, "退款失败");
                }
            }
        }

        //更改平台的订单状态---已取消
        orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
        this.updateById(orderInfo);

        //向MQ发送消息  异步更新号源数量+发送短信通知
        OrderMqVo orderMqVo = new OrderMqVo();
        orderMqVo.setScheduleId(orderInfo.getScheduleId());
        MsmVo msmVo = new MsmVo();
        msmVo.setPhone(orderInfo.getPatientPhone());
        msmVo.getParam().put("message", "取消挂号成功");
        orderMqVo.setMsmVo(msmVo);
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER, MqConst.ROUTING_ORDER, orderMqVo);

        return true;
    }

    /**
     * 就医提醒
     */
    @Override
    public void patientTips() {
        //查询今天的订单
        QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("reserve_date", new DateTime().toString("yyyy-MM-dd"));
        List<OrderInfo> orderInfoList = baseMapper.selectList(queryWrapper);

        //遍历 orderInfoList  发送短信提醒
        for (OrderInfo orderInfo : orderInfoList) {
            //短信提示
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            msmVo.getParam().put("message", "请您及时就医，以免耽误病情");
            //放入短信服务监听的队列 用于给客户发送短信
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM, msmVo);

        }
    }

    /**
     * 订单统计
     * @param orderCountQueryVo  查询条件
     * @return
     */
    @Override
    public Map<String, Object> getCountMap(OrderCountQueryVo orderCountQueryVo) {

        Map<String,Object> map = new HashMap<>();
        List<OrderCountVo> orderQueryVoList = baseMapper.selectOrderCount(orderCountQueryVo);

        //日期列表 和 统计列表
        ArrayList<String> xList = new ArrayList<>();
        ArrayList<Integer> yList = new ArrayList<>();
        for (OrderCountVo orderCountVo : orderQueryVoList) {
            String reserveDate = orderCountVo.getReserveDate();
            xList.add(reserveDate);
            Integer count = orderCountVo.getCount();
            yList.add(count);
        }
        map.put("dateList", xList);
        map.put("countList", yList);
        return map;
    }

    /**
     * 封装orderStatusString
     * @param orderInfo
     */
    private void packOrderInfo(OrderInfo orderInfo) {
        String orderStatusString = OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus());
        orderInfo.getParam().put("orderStatusString", orderStatusString);
    }
}
